﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using Microsoft.SharePoint;
using Microsoft.Office.Server;
using Microsoft.Office.Server.Search.Administration;
using Microsoft.Win32;
using System.Configuration;

namespace SearchCrawlLogViewer
{
    public partial class MainForm : Form
    {
        CrawlLogFilterContext crawlLogFilterContext = null;

        /// <summary>
        /// MainForm constructor. Initializes combobxes.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            comboBoxContentSources_Init();
            comboBoxStatusMessages_Init();

            comboBoxStatusTypes.SelectedIndex = 0;
            comboBoxHourEarlierThan.SelectedIndex = 0;
            comboBoxHourLaterThan.SelectedIndex = 0;
            comboBoxMinuteEarlierThan.SelectedIndex = 0;
            comboBoxMinuteLaterThan.SelectedIndex = 0;
            
            buttonLoadSearchContext_SetState();
        }

        /// <summary>
        /// Handles the click event for the Load Search Context button. Creates a SearchContext
        /// object for a specified site and retrieves the associated content sources and
        /// status messages.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonLoadSearchContext_Click(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;
                dataGridViewResults.DataSource = null;

                comboBoxContentSources_Init();
                comboBoxStatusMessages_Init();

				SearchService searchService = SearchService.Service;
				SearchServiceApplication contentSSA = null;
				string ssaName = ConfigurationManager.AppSettings["fastContentSSAName"];
				// See if the SSA name is explicitly configured
				if (!string.IsNullOrEmpty(ssaName))
				{
					contentSSA = searchService.SearchApplications.GetValue<SearchServiceApplication>(ssaName);
				}
				else // If not, go for the FAST Content SSA by default
				{
					foreach (SearchServiceApplication item in searchService.SearchApplications)
					{
						// Only the FAST Content SSA has this value. FAST Query SSA and the SP SSA don't
						if (item.SearchApplicationType == SearchServiceApplicationType.ExtendedConnector)
						{
							contentSSA = item;
							break;
						}
					}
				}
				if (contentSSA == null)
				{
					throw new Exception("Cannot find the FAST Content SSA");
				}
                // Create an instance of the CrawlLogFilterContext class which
                // wraps instances of the CrawlLogFilters and LogViewer classes and
                // caches viewed results.
				crawlLogFilterContext = new CrawlLogFilterContext(contentSSA);

                // Get the search context's content sources and add them to the
                // combobox to allow filtering by content source.
				Content content = new Content(contentSSA);
                    
                foreach (ContentSource cs in content.ContentSources)
                    comboBoxContentSources.Items.Add(new ContentSourceWrapper(cs.Name, cs.Id));

                // Get all status messages for the search context and add them to the
                // combobox to allow filtering by status message.
                DataTable allStatusMessages =
                    crawlLogFilterContext.LogViewer.GetAllStatusMessages();

                foreach (DataRow row in allStatusMessages.Rows)
                    if ((row["ErrorMsg"]).ToString() != String.Empty)
                        comboBoxStatusMessages.Items.Add(
                            new StatusMessageWrapper((row["ErrorMsg"]).ToString(), Convert.ToInt32(row["ErrorId"])));

                // Display the search application's friendly name and ID.
				labelApplicationName.Text = contentSSA.Name;
                labelDisplayName.Text = Registry.GetValue(@"HKEY_LOCAL_MACHINE\Software\Microsoft\Office Server\14.0\Search\Applications\" +
						contentSSA.ApplicationName + @"\CatalogNames", "DisplayName", string.Empty).ToString();

                // Enable the Filter button since we now have a search context.
                buttonFilter.Enabled = true;
            }
            catch (Exception ex)
            {
                labelApplicationName.Text = string.Empty;
                labelDisplayName.Text = string.Empty;
                dataGridViewResults.DataSource = null;
                buttonFilter.Enabled = false;

                MessageBox.Show(ex.Message, "Search Crawl Log Viewer", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                buttonPrevious.Visible = false;
                labelItemRange.Visible = false;
                buttonNext.Visible = false;

                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Initializes Content Sources combobox.
        /// </summary>
        private void comboBoxContentSources_Init()
        {
            comboBoxContentSources.Items.Clear();
            comboBoxContentSources.Items.Add(new ContentSourceWrapper("All", -1));
            comboBoxContentSources.SelectedIndex = 0;
        }

        /// <summary>
        ///  Initializes Status Messages combobox.
        /// </summary>
        private void comboBoxStatusMessages_Init()
        {
            comboBoxStatusMessages.Items.Clear();
            comboBoxStatusMessages.Items.Add(new StatusMessageWrapper("All", -1));
            comboBoxStatusMessages.SelectedIndex = 0;
        }

        /// <summary>
        /// Handles the click event for the Filter button. Adds the selected
        /// filters to the CrawlLogFilterContext object's collection of filters
        /// and calls the GetCrawlLogData method to retrieve the filtered view.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonFilter_Click(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                // Clear the results grid.
                dataGridViewResults.DataSource = null;

                // If the global CrawlLogFilterContext is not null...
                if (crawlLogFilterContext != null)
                {
                    // Clear the crawl log data cache and any previously added crawl log filters
                    // and reset the current results page number.
                    crawlLogFilterContext.ResetCrawlLogDataCache();
                    crawlLogFilterContext.ResetCrawlLogFilters();
                    crawlLogFilterContext.CurrentPageNumber = 1;
                    
                    // Add the portal content filter.
                    crawlLogFilterContext.CatalogType = CatalogType.PortalContent;

                    // Add additional filters based on the user's selections.

                    // Message Type filter
                    switch (comboBoxStatusTypes.Text)
                    {
                        case "Error":
                            crawlLogFilterContext.MessageType = MessageType.Error;
                            break;

                        case "Success":
                            crawlLogFilterContext.MessageType = MessageType.Success;
                            break;

                        case "Warning":
                            crawlLogFilterContext.MessageType = MessageType.Warning;
                            break;
                    }

                    // Hostname filter
                    if (textBoxHostnamePath.Text != string.Empty)
                        crawlLogFilterContext.HostNamePath = textBoxHostnamePath.Text;

                    // Content Source
                    if (comboBoxContentSources.Text != "All")
                        crawlLogFilterContext.ContentSourceID =
                            ((ContentSourceWrapper)comboBoxContentSources.SelectedItem).ContentSourceID;

                    // Status Message
                    if (comboBoxStatusMessages.Text != "All")
                        crawlLogFilterContext.StatusMessageId =
                            ((StatusMessageWrapper)comboBoxStatusMessages.SelectedItem).StatusMessageID;

                    // Entries Earlier than
                    DateTime endLogTime = DateTime.MaxValue;    

                    if (textBoxDateEarlierThan.Text != string.Empty)
                    {
                        try
                        {
                            endLogTime = DateTime.Parse(textBoxDateEarlierThan.Text);

                            endLogTime = BuildDateTime(endLogTime, comboBoxHourEarlierThan.Text, comboBoxMinuteEarlierThan.Text);
                        }
                        catch (Exception ex1)
                        {
                            textBoxDateEarlierThan.Focus();
                            throw new Exception("There is a problem with the messages occuring earlier than filter. " + ex1.Message);
                        }
                    }

                    // Entries later than
                    DateTime startLogTime = DateTime.MinValue;
                    if (textBoxDateLaterThan.Text != string.Empty)
                    {
                        try
                        {
                            startLogTime = DateTime.Parse(textBoxDateLaterThan.Text);

                            startLogTime = BuildDateTime(startLogTime, comboBoxHourLaterThan.Text, comboBoxMinuteLaterThan.Text);
                        }
                        catch (Exception ex2)
                        {
                            textBoxDateLaterThan.Focus();
                            throw new Exception("There is a problem with the messages occuring later than filter. " + ex2.Message);
                        }
                    }

                    crawlLogFilterContext.SetLogTimeFilters(startLogTime, endLogTime);

                    // Initialize the paging related values.
                    crawlLogFilterContext.StartAt = 1;
					crawlLogFilterContext.TotalEntries = int.Parse(cboPageSize.Text);

                    // Get the results based on the current filters.
                    GetCrawlLogData();
                }
                else
                    throw new Exception("Please load a search context before trying to filter the crawl log.");
            }
            catch (Exception ex3)
            {
                MessageBox.Show(ex3.Message, "Search Crawl Log Viewer", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Gets the crawl log data using the specified filters. 
        /// </summary>
        private void GetCrawlLogData()
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                CrawlLogData crawlLogData = crawlLogFilterContext.GetCrawlLogData();

                // Bind the results grid to the crawl log data.
                dataGridViewResults.DataSource = crawlLogData.CrawlLogDataTable;

                // Set the states of the results paging controls.
                SetIndexingControlsStates(crawlLogData);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Sets the states of the results paging controls.
        /// </summary>
        private void SetIndexingControlsStates(CrawlLogData crawlLogData)
        {
            if (crawlLogData == null)
            {
                // Hide the paging controls.
                buttonPrevious.Visible = false;
                labelItemRange.Visible = false;
                buttonNext.Visible = false;
            }
            else
            {
                // If the last page of results is being displayed then ...
                if (crawlLogData.IsLastPage)
                {
                    // disable the Next button and display the disabled version of the button image.
                    buttonNext.Enabled = false;
                    buttonNext.BackgroundImage = global::SearchCrawlLogViewer.Properties.Resources.nextdisabled;
                }
                else
                {
                    // enable the Next button and display the enabled version of the button image.
                    buttonNext.Enabled = true;
                    buttonNext.BackgroundImage = global::SearchCrawlLogViewer.Properties.Resources.next;
                }

                // If the first page of results is being displayed then ...
                if (crawlLogData.IsFirstPage)
                {
                    // Disable the Previous button and display the disabled version of the button image.
                    buttonPrevious.Enabled = false;
                    buttonPrevious.BackgroundImage = global::SearchCrawlLogViewer.Properties.Resources.previousdisabled;
                }
                else
                {
                    // Enable the Previous button and display the enabled version of the button image.
                    buttonPrevious.Enabled = true;
                    buttonPrevious.BackgroundImage = global::SearchCrawlLogViewer.Properties.Resources.previous;
                }

                // If crawl log data results have been returned then ...
                if (crawlLogFilterContext.DoesCrawlLogDataExist())
                {
                    // Set the text showing the starting and ending item indexes and show the paging controls.
                    if (crawlLogData.ItemCount == 0)
                        labelItemRange.Text = "No Matches";
                    else
                        labelItemRange.Text =
                            crawlLogData.StartIndex.ToString() + " - " + crawlLogData.EndIndex.ToString();

                    labelItemRange.Visible = true;
                    buttonPrevious.Visible = true;
                    buttonNext.Visible = true;
                }
                else
                {
                    // Hide the paging controls.
                    buttonPrevious.Visible = false;
                    labelItemRange.Visible = false;
                    buttonNext.Visible = false;
                }
            }
        }

        /// <summary>
        /// Sets the state of the Load Search Context button.
        /// </summary>
        private void buttonLoadSearchContext_SetState()
        {
            // Enable the button if the site URL textbox is not empty.
            buttonLoadSearchContext.Enabled = (textBoxSiteURL.Text != string.Empty);
        }

        /// <summary>
        /// Handles the TextChanged event of the site url textbox.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void textBoxSiteURL_TextChanged(object sender, EventArgs e)
        {
            // Set the Load Search Context button state.
            buttonLoadSearchContext_SetState();            
        }

        /// <summary>
        /// Handles the Click event of the Calendar control for specifying the
        /// log entry start date.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonCalendarLaterThan_Click(object sender, EventArgs e)
        {
            // Toggle the visibility of the calendar.
            monthCalendarLaterThan.Visible = !monthCalendarLaterThan.Visible;
        }

        /// <summary>
        /// Handles the Click event of the Calendar control for specifying the
        /// log entry end date.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonCalendarEarlierThan_Click(object sender, EventArgs e)
        {
            // Toggle the visibility of the calendar.
            monthCalendarEarlierThan.Visible = !monthCalendarEarlierThan.Visible;
        }

        /// <summary>
        /// Handles the DateSelected event of the Calendar control for the log entry start date. 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void monthCalendarLaterThan_DateSelected(object sender, DateRangeEventArgs e)
        {
            // Put the selected date into the Later Than text box and hide the calendar.
            textBoxDateLaterThan.Text = e.Start.ToShortDateString();
            monthCalendarLaterThan.Visible = false;
        }

        /// <summary>
        /// Handles the DateSelected event of the Calendar control for the log entry end date. 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void monthCalendarEarlierThan_DateSelected(object sender, DateRangeEventArgs e)
        {
            // Put the selected date into the Earlier Than text box and hide the calendar.
            textBoxDateEarlierThan.Text = e.Start.ToShortDateString();
            monthCalendarEarlierThan.Visible = false;
        }

        /// <summary>
        ///  Handles the Click event of the Previous results page button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonPrevious_Click(object sender, EventArgs e)
        {
            // Decrement the current page number and go get the crawl data.
            crawlLogFilterContext.CurrentPageNumber--;
            GetCrawlLogData();
        }

        /// <summary>
        ///  Handles the Click event of the Next results page button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonNext_Click(object sender, EventArgs e)
        {
            // Increment the current page number and go get the crawl data.
            crawlLogFilterContext.CurrentPageNumber++;
            GetCrawlLogData();
        }

        /// <summary>
        /// Helper for building a DateTime from the values selected by the user.
        /// </summary>
        /// <param name="datePart">The date portion to combine.</param>
        /// <param name="hourPart">The hour portion to combine.</param>
        /// <param name="minutePart">The minute portion to combine.</param>
        /// <returns>A DateTime instance that combines the provided date and time parts.</returns>
        public DateTime BuildDateTime(DateTime datePart, string hourPart, string minutePart)
        {
            DateTime newDateTime;
            int Hours = 0;
            int Minutes = 0;

            switch (hourPart)
            {
                case "1 AM": Hours = 1;
                    break;
                case "2 AM": Hours = 2;
                    break;
                case "3 AM": Hours = 3;
                    break;
                case "4 AM": Hours = 4;
                    break;
                case "5 AM": Hours = 5;
                    break;
                case "6 AM": Hours = 6;
                    break;
                case "7 AM": Hours = 7;
                    break;
                case "8 AM": Hours = 8;
                    break;
                case "9 AM": Hours = 9;
                    break;
                case "10 AM": Hours = 10;
                    break;
                case "11 AM": Hours = 11;
                    break;
                case "12 PM": Hours = 12;
                    break;
                case "1 PM": Hours = 13;
                    break;
                case "2 PM": Hours = 14;
                    break;
                case "3 PM": Hours = 15;
                    break;
                case "4 PM": Hours = 16;
                    break;
                case "5 PM": Hours = 17;
                    break;
                case "6 PM": Hours = 18;
                    break;
                case "7 PM": Hours = 19;
                    break;
                case "8 PM": Hours = 20;
                    break;
                case "9 PM": Hours = 21;
                    break;
                case "10 PM": Hours = 22;
                    break;
                case "11 PM": Hours = 23;
                    break;
            }

            newDateTime = datePart.AddHours(Hours);

            switch (minutePart)
            {
                case "05": Minutes = 5;
                    break;
                case "10": Minutes = 10;
                    break;
                case "15": Minutes = 12;
                    break;
                case "20": Minutes = 20;
                    break;
                case "25": Minutes = 25;
                    break;
                case "30": Minutes = 30;
                    break;
                case "35": Minutes = 35;
                    break;
                case "40": Minutes = 40;
                    break;
                case "45": Minutes = 45;
                    break;
                case "50": Minutes = 50;
                    break;
                case "55": Minutes = 55;
                    break;
            }

            newDateTime = newDateTime.AddMinutes(Minutes);

            return newDateTime;
        }

    }
}
